package handler

import (
	"encoding/base64"
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v4"
	"github.com/pkg/errors"
	"strconv"
	"strings"
	"yxs-mall/common/models"
)

const mySecret = "X_N_Z_P"

type MyClaims struct {
	Aud                  string `json:"aud"`
	IsAuth               string `json:"isAuth"`
	Role                 string `json:"role"`
	jwt.RegisteredClaims        // 注意!这是jwt-go的v4版本新增的，原先是jwt.StandardClaims
}

func SetContextCurrentUser(c *gin.Context, claims *MyClaims) {
	userId, _ := strconv.ParseInt(claims.Aud, 10, 64)
	isAuth, _ := strconv.Atoi(claims.IsAuth)
	role, _ := strconv.Atoi(claims.Role)
	user := models.CurrentUser{
		UserId: userId, IsAuth: isAuth, Role: role,
	}
	c.Set("currentUser", user)
}

func ParseToken(tokens string) (*MyClaims, error) {
	var userId string
	var err error
	if userId, err = getAudience(tokens); err != nil {
		return nil, err
	}

	token, err := jwt.ParseWithClaims(tokens, &MyClaims{}, secret(userId))
	if err != nil {
		println(err.Error())
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, errors.New("that's not even a token")
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, errors.New("token is expired")
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, errors.New("token not active yet")
			} else {
				return nil, errors.New("couldn't handle this token")
			}
		}
	}
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, errors.New("couldn't handle this token")
}

func secret(prefix string) jwt.Keyfunc {
	return func(token *jwt.Token) (interface{}, error) {
		return []byte(prefix + mySecret), nil // 这是我的secret
	}
}

func getAudience(token string) (aud string, err error) {
	parts := strings.Split(token, ".")
	if len(parts) != 3 {
		return "", errors.New("token contains an invalid number of segments")
	}
	var decodeBytes []byte
	if decodeBytes, err = base64.URLEncoding.DecodeString(parts[1]); err != nil {
		return "", errors.New("token aud an invalid")
	}
	payload := make(map[string]interface{})
	err = json.Unmarshal(decodeBytes, &payload)
	if err = json.Unmarshal(decodeBytes, &payload); err != nil {
		return "", err
	}
	aud = payload["aud"].(string)
	return aud, nil
}
